﻿using Microsoft.CSharp.RuntimeBinder;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using WJP.Utils;

namespace WJP.DataPortal.WCF
{
    /// <summary>
    /// 
    /// </summary>
    public class HostManager
    {
        /// <summary>
        /// 
        /// </summary>
        public static HostManager Default = new HostManager();
        private HostInfo[] hosts = new HostInfo[0];
        private int[] indexs = new int[0];
        private object lockObj = new object();
        private Random random = new Random();
        private int retryTimes = ConfigurationHelper.GetAppSettingOrDefault("Host.RetryTimes", 5);

        static CallSite<Func<CallSite, object, HostInfo>> DefaultSite;
        static CallSite<Func<CallSite, object, bool>> callSuccess;
        static CallSite<Func<CallSite, object, double, object>> doubleResult;
        static CallSite<Func<CallSite, object, object>> objectResult;
        static CallSite<Func<CallSite, object, bool>> failCall;
        static CallSite<Func<CallSite, object, object, object>> objcall;
        static CallSite<Func<CallSite, object, double, object>> docall;
        static CallSite<Func<CallSite, object, object>> objobjCall;
        static CallSite<Func<CallSite, object, object>> objtocall;

        /// <summary>
        /// 所有注册的Host
        /// </summary>
        public IList<HostInfo> Hosts
        {
            get
            {
                return this.hosts;
            }
        }

        /// <summary>
        /// 获取Host信息
        /// </summary>
        /// <returns></returns>
        public HostInfo GetHost()
        {
            HostInfo[] array = hosts.Where(p => p.IsActived && !p.IsStop).ToArray();
            if (array.Length == 0)
                return null;

            double performances = array.Sum(p => p.Performance);
            List<object> list = new List<object>();
            double fromSection = 0.0;
            foreach (var hostInfo in array)
            {
                double weight = hostInfo.Performance / performances;//权重
                list.Add(new
                {
                    Host = hostInfo,
                    From = fromSection,
                    To = fromSection + weight
                });
                fromSection += weight;
            }
            double offset = random.NextDouble();
            if (DefaultSite == null)
            {
                DefaultSite = CallSite<Func<CallSite, object, HostInfo>>.Create(Binder.Convert(CSharpBinderFlags.None, typeof(HostInfo), typeof(HostManager)));
            }
            Func<CallSite, object, HostInfo> target = DefaultSite.Target;
            CallSite defaultSite2 = DefaultSite;
            object obj = list.FirstOrDefault(delegate (dynamic p)
            {
                if (callSuccess == null)
                {
                    callSuccess = CallSite<Func<CallSite, object, bool>>.Create(Binder.Convert(CSharpBinderFlags.None, typeof(bool), typeof(HostManager)));
                }
                Func<CallSite, object, bool> target2 = callSuccess.Target;
                CallSite callSuccess2 = callSuccess;
                if (doubleResult == null)
                {
                    doubleResult = CallSite<Func<CallSite, object, double, object>>.Create(Binder.BinaryOperation(CSharpBinderFlags.None, ExpressionType.LessThanOrEqual, typeof(HostManager), new CSharpArgumentInfo[]
                    {
                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
                    }));
                }
                Func<CallSite, object, double, object> target3 = doubleResult.Target;
                CallSite doubleResult2 = doubleResult;
                if (objectResult == null)
                {
                    objectResult = CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember(CSharpBinderFlags.None, "From", typeof(HostManager), new CSharpArgumentInfo[]
                    {
                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                    }));
                }
                object obj2 = target3(doubleResult2, objectResult.Target(objectResult, p), offset);
                if (failCall == null)
                {
                    failCall = CallSite<Func<CallSite, object, bool>>.Create(Binder.UnaryOperation(CSharpBinderFlags.None, ExpressionType.IsFalse, typeof(HostManager), new CSharpArgumentInfo[]
                    {
                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                    }));
                }
                object arg3;
                if (!failCall.Target(failCall, obj2))
                {
                    if (objcall == null)
                    {
                        objcall = CallSite<Func<CallSite, object, object, object>>.Create(Binder.BinaryOperation(CSharpBinderFlags.BinaryOperationLogical, ExpressionType.And, typeof(HostManager), new CSharpArgumentInfo[]
                        {
                                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                        }));
                    }
                    Func<CallSite, object, object, object> target4 = objcall.Target;
                    CallSite objcall2 = objcall;
                    object arg2 = obj2;
                    if (docall == null)
                    {
                        docall = CallSite<Func<CallSite, object, double, object>>.Create(Binder.BinaryOperation(CSharpBinderFlags.None, ExpressionType.GreaterThanOrEqual, typeof(HostManager), new CSharpArgumentInfo[]
                        {
                                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null)
                        }));
                    }
                    Func<CallSite, object, double, object> target5 = docall.Target;
                    CallSite docall2 = docall;
                    if (objobjCall == null)
                    {
                        objobjCall = CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember(CSharpBinderFlags.None, "To", typeof(HostManager), new CSharpArgumentInfo[]
                        {
                                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                        }));
                    }
                    arg3 = target4(objcall2, arg2, target5(docall2, objobjCall.Target(objobjCall, p), offset));
                }
                else
                {
                    arg3 = obj2;
                }
                return target2(callSuccess2, arg3);
            });
            object arg;
            if (obj == null)
            {
                arg = null;
            }
            else
            {
                if (objtocall == null)
                {
                    objtocall = CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember(CSharpBinderFlags.None, "Host", typeof(HostManager), new CSharpArgumentInfo[]
                    {
                            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                    }));
                }
                arg = objtocall.Target(objtocall, obj);
            }
            var result = target(defaultSite2, arg);

            return result;
        }

        /// <summary>
        /// 注册子服务
        /// </summary>
        /// <param name="subHost"></param>
        public void RegisterHost(SubHost subHost)
        {
            lock (lockObj)
            {
                List<HostInfo> source = new List<HostInfo>(hosts);
                HostInfo hostInfo = source.FirstOrDefault(p => p.Address.CIEquals(subHost.Address));
                if(hostInfo != null)
                {
                    hostInfo.Performance = subHost.Performance;
                    hostInfo.Fail = 0L;
                    hostInfo.IsActived = true;
                }
                else
                {
                    hostInfo = new HostInfo()
                    {
                        Address = subHost.Address,
                        Master = Dns.GetHostName(),
                        IsActived = true,
                        Performance = subHost.Performance,
                    };
                    source.Add(hostInfo);
                }
                hosts = source.Distinct(HostInfoComparer.Instance).ToArray();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="host"></param>
        public void Success(HostInfo host)
        {
            host.Success += 1;
            host.Fail = 0L;
        }

        /// <summary>
        /// Host请求失败
        /// </summary>
        /// <param name="host"></param>
        public void Faild(HostInfo host)
        {
            host.Fail++;
            host.TotalFail++;
            if (host.Fail <= retryTimes)
                return;
            host.IsActived = false;
        }

        /// <summary>
        /// 更新Host信息
        /// </summary>
        /// <param name="hostInfo"></param>
        public void Update(HostInfo hostInfo)
        {
            var host = hosts.FirstOrDefault(p => p.Address.CIEquals(hostInfo.Address));
            if (host == null)
                return;
            host.IsActived = hostInfo.IsActived;
            host.Performance = hostInfo.Performance;
        }

        /// <summary>
        /// 启动服务
        /// </summary>
        /// <param name="address">服务地址</param>
        /// <returns></returns>
        internal bool Start(string address)
        {
            var hostInfo = hosts.FirstOrDefault(p => p.Address.CIEquals(address));
            if (hostInfo == null)
                return false;
            hostInfo.IsStop = false;
            return true;
        }

        /// <summary>
        /// 停止服务
        /// </summary>
        /// <param name="address">服务地址</param>
        /// <returns></returns>
        internal bool Stop(string address)
        {
            var hostInfo = hosts.FirstOrDefault(p => p.Address.CIEquals(address));
            if (hostInfo == null)
                return false;
            hostInfo.IsStop = true;
            return true;
        }
    }
}
